package com.cml.domain.service;

import com.cml.common.config.Configuration;
import com.cml.common.constant.NodeStatus;
import com.cml.common.context.ServerContext;
import com.cml.common.dto.entity.SoltMetadata;
import com.cml.domain.entity.ServerNode;
import com.cml.infrastructure.MessageQueueManager;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 姓名：陈茂林
 * 时间：2023/11/24 16:13
 * 描述：
 */
public class ServerNodeHAManagerService {
    private  ServerContext serverContext;
    private ServerNodeManagerService serverNodeManagerService;

    private SoltContextManagerService soltContextManagerService;

    private List<ServerNode > disconnectionServerNodeList = new ArrayList<>();
    private
    Logger logger = Logger.getLogger(ServerNodeHAManagerService.class);

    private ServerNodeHAManagerService(){
        new HAConnectThread().start();
    }

    public static ServerNodeHAManagerService getInstance(ServerContext serverContext){
        if(Singleton.serverNodeHAManagerService.serverContext==null){
            Singleton.serverNodeHAManagerService.serverContext = serverContext;
            Singleton.serverNodeHAManagerService. serverNodeManagerService=serverContext.getServerNodeManagerService();
            Singleton.serverNodeHAManagerService.soltContextManagerService =serverContext.getSoltManagerService();;
        }
        return Singleton.serverNodeHAManagerService;
    }

    public void ha(ServerNode disconnectionServerNode){
        disconnectionServerNodeList.add(disconnectionServerNode);
        disconnectionServerNode = serverContext.getNetworkManager().getRemoteServerNodeManager().getServerNode(disconnectionServerNode.getId());
        if(disconnectionServerNode != null) {
            NodeStatus.getInstance().setCurStatus(NodeStatus.NODE_HA);
            logger.error("进入高可用故障模式" + disconnectionServerNode);
            clearResource(disconnectionServerNode);
            //leader
            if (disconnectionServerNode.isLeader()) {
                //leder重新选举
                Integer leader = serverNodeManagerService.leaderElect();
                logger.info("重新选举的leader:" + leader);
            }
            if (serverContext.getServerNode().isLeader()) {
                //重构副本关系
                buildDisconnectionServerNodeSoltMetadata(disconnectionServerNode);
            }
            soltContextManagerService.startSoltManagerProcess();
            NodeStatus.getInstance().setCurStatus(NodeStatus.NODE_RUN);
        }else{
            logger.info("重复断联处理！");
        }

    }

    private void buildDisconnectionServerNodeSoltMetadata(ServerNode disconnectionServerNode){
        //断联槽对应关系移出
        //断链节点的solt元数据
        List<SoltMetadata> soltMetadataList = soltContextManagerService.getSlotsAllocation();
        SoltMetadata disconnectNodeMySoltMetadata =getDisconnectNodeSoltMetadata(disconnectionServerNode,soltMetadataList);
        logger.info("断连节点元数据为："+disconnectNodeMySoltMetadata);

        //断链节点的solt元数据，副本转正
        List<SoltMetadata> soltMetadataReplicasList = soltContextManagerService.getSlotsReplicasAllocation();
        SoltMetadata disconnectNodeMySoltMetadataReplicas =getDisconnectNodeSoltReplicasMetadata(disconnectionServerNode.getId(),soltMetadataReplicasList);
        logger.info("断连节点元数据副本为："+disconnectNodeMySoltMetadataReplicas);

        //获取断联节点所在副本节点的副本
        SoltMetadata replicasNodeToReplicasNodeMetadata=getDisconnectNodeSoltReplicasMetadata(disconnectNodeMySoltMetadataReplicas.getServerNodeId(),soltMetadataReplicasList);


        disconnectNodeMySoltMetadata.setServerNodeId(disconnectNodeMySoltMetadataReplicas.getServerNodeId());
        logger.info("断连节点元数据指定到新节点:" + disconnectNodeMySoltMetadata.getServerNodeId());

        //断链节点所保存的副本solt元数据
        SoltMetadata disconnectNodeMyReplicasNodeSoltMetadata =getDisconnectNodeReplicasSoltMetadata(disconnectionServerNode.getId(),soltMetadataReplicasList);
        logger.info("断连节点元副本数据为："+disconnectNodeMyReplicasNodeSoltMetadata);

        disconnectNodeMyReplicasNodeSoltMetadata.setServerNodeId(disconnectNodeMySoltMetadataReplicas.getServerNodeId());
        logger.info("更改节点id"+disconnectNodeMyReplicasNodeSoltMetadata.getReplicasTargetServerNode()+"副本到"+disconnectNodeMySoltMetadataReplicas.getServerNodeId());


        //设置断连的副本新指向
        disconnectNodeMySoltMetadataReplicas.setReplicasTargetServerNode(replicasNodeToReplicasNodeMetadata.getReplicasTargetServerNode());
        disconnectNodeMyReplicasNodeSoltMetadata.setServerNodeId(replicasNodeToReplicasNodeMetadata.getServerNodeId());
        logger.info("断联节点副本以转移到原副本所在的节点"+disconnectNodeMySoltMetadataReplicas);

        logger.info("------------------重构后的元数据-------------------");
        logger.info(soltMetadataList);
        logger.info(soltMetadataReplicasList);
        logger.info("-------------------------------------");
    }
    private SoltMetadata getDisconnectNodeReplicasSoltMetadata(Integer serverNodeId,List<SoltMetadata> soltMetadataList){
        for(SoltMetadata soltMetadata : soltMetadataList){
            if(serverNodeId.equals(soltMetadata.getServerNodeId())){
                return soltMetadata;
            }
        }
        return null;
    }

    private SoltMetadata getDisconnectNodeSoltReplicasMetadata(Integer serverNodeId,List<SoltMetadata> soltMetadataList){
        for(SoltMetadata soltMetadata : soltMetadataList){
            if(serverNodeId.equals(soltMetadata.getReplicasTargetServerNode())){
                return soltMetadata;
            }
        }
        return null;
    }

    private SoltMetadata getDisconnectNodeSoltMetadata(ServerNode disconnectionServerNode,List<SoltMetadata> soltMetadataList){
        for(SoltMetadata soltMetadata : soltMetadataList){
            if(disconnectionServerNode.getId().equals(soltMetadata.getServerNodeId())){
                return soltMetadata;
            }
        }
        return null;
    }



    private void clearResource(ServerNode remoteServerNode) {
        this.serverContext.getNetworkManager().clearResource(remoteServerNode);
        this.serverContext.getNetworkManager().clearIOThread(remoteServerNode);
        MessageQueueManager.removeSendQueue(remoteServerNode.getId());
    }

    private static class Singleton{
        public static ServerNodeHAManagerService serverNodeHAManagerService = new ServerNodeHAManagerService();
    }

    class HAConnectThread extends  Thread{
        @Override
        public void run() {
            //断联节点
            //1、follower 启动时连接所有follower节点
            //2、obsever 不断连接observer节点

            while (true){
                for (int i = 0; i < disconnectionServerNodeList.size(); i++) {
                    ServerNode serverNode = disconnectionServerNodeList.get(i);
                    if(serverNode.isFollower()){
                        //当前节点是follower，连接比自己小的
                        if(serverContext.getServerNode().isFollower()){
                            connCurNodeBeforeFollower(serverNode);
                        }else{
                            connDisconnectionObseverNode(serverNode);
                        }
                    }
                    boolean isRemove = removeDisconnectionServerNodeConnectionStatus(serverNode);
                    //todo 通知leader进入Rebalance模式
                    if(isRemove){
                        NodeStatus.getInstance().setCurStatus(NodeStatus.NODE_HA);
                        //ServerNodeRebalanceService.getInstance(serverContext).waitNodeRebalance(serverNode);
                    }

                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    logger.error(e.getMessage(),e);
                }
            }
            
        }

        private boolean connDisconnectionObseverNode(ServerNode disconnectionServerNode){
            Map map = new HashMap();
            map.put("ip",disconnectionServerNode.getIp());
            map.put("port",disconnectionServerNode.getPort());
            return serverContext.getNetworkManager().sendSocketConn(map);

        }
        private boolean removeDisconnectionServerNodeConnectionStatus(ServerNode disconnectionServerNode){
            ServerNode serverNode = serverContext.getNetworkManager().getRemoteServerNodeManager().getServerNode(disconnectionServerNode.getId());
            if(serverNode != null){
                disconnectionServerNodeList.remove(disconnectionServerNode);
                return true;
            }
            return false;
        }

        private boolean connCurNodeBeforeFollower(ServerNode disconnectionServerNode){
            Configuration configuration = Configuration.getInstance();
            List<Map> nodeFollowerServerList =configuration.getBeforeFollowerServerList();
            for(Map map :nodeFollowerServerList){
                //判断在我前面的节点是否包含断联节点
                if(disconnectionServerNode.getIp().equals(map.get("ip")) && disconnectionServerNode.getPort().equals(map.get("port"))){
                    boolean connStatus = serverContext.getNetworkManager().sendSocketConn(map);
                    return connStatus;
                }
            }
            return false;
        }
    }
}
